/* w_sprite.c
 *
 * Micropolis, Unix Version.  This game was released for the Unix platform
 * in or about 1990 and has been modified for inclusion in the One Laptop
 * Per Child program.  Copyright (C) 1989 - 2007 Electronic Arts Inc.  If
 * you need assistance with this program, you may contact:
 *   http://wiki.laptop.org/go/Micropolis  or email  micropolis@laptop.org.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.  You should have received a
 * copy of the GNU General Public License along with this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 * 
 *             ADDITIONAL TERMS per GNU GPL Section 7
 * 
 * No trademark or publicity rights are granted.  This license does NOT
 * give you any right, title or interest in the trademark SimCity or any
 * other Electronic Arts trademark.  You may not distribute any
 * modification of this program using the trademark SimCity or claim any
 * affliation or association with Electronic Arts Inc. or its employees.
 * 
 * Any propagation or conveyance of this program must include this
 * copyright notice and these terms.
 * 
 * If you convey this program (or any modifications of it) and assume
 * contractual liability for the program to recipients of it, you agree
 * to indemnify Electronic Arts for any liability that those contractual
 * assumptions impose on Electronic Arts.
 * 
 * You may not misrepresent the origins of this program; modified
 * versions of the program must be marked as such and not identified as
 * the original program.
 * 
 * This disclaimer supplements the one included in the General Public
 * License.  TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS
 * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY
 * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK.  THE ENTIRE RISK OF
 * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU.  ELECTRONIC ARTS
 * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES,
 * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY
 * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING,
 * USAGE, OR TRADE PRACTICE.  ELECTRONIC ARTS DOES NOT WARRANT AGAINST
 * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL
 * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE
 * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE
 * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE
 * CORRECTED.  NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR
 * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY.  SOME
 * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED
 * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
 * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY
 * NOT APPLY TO YOU.
 */
#include "sim.h"

Tcl_HashTable SpriteCmds;
short CrashX, CrashY;
int absDist;
short Cycle;

SimSprite *GlobalSprites[OBJN];

SimSprite *NewSprite(char *name, int type, int x, int y);
void MonsterHere(int x, int y);
void MakeShipHere(int x, int y);
void StartFire(int x, int y);
void OFireZone(int Xloc, int Yloc, int ch);
void Destroy(int ox, int oy);
void ExplodeSprite(SimSprite * sprite);
int CanDriveOn(int x, int y);
void DoBusSprite(SimSprite * sprite);
void DoExplosionSprite(SimSprite * sprite);
void DoTornadoSprite(SimSprite * sprite);
void DoMonsterSprite(SimSprite * sprite);
void DoShipSprite(SimSprite * sprite);
void DoAirplaneSprite(SimSprite * sprite);
void DoCopterSprite(SimSprite * sprite);
void DoTrainSprite(SimSprite * sprite);
void DrawSprite(SimView * view, SimSprite * sprite);
void InitSprite(SimSprite * sprite, int x, int y);

#define TRA_GROOVE_X -39
#define TRA_GROOVE_Y 6
#define BUS_GROOVE_X -39
#define BUS_GROOVE_Y 6

#define SPRITECMD_ACCESS_INT(var) \
  int SpriteCmd##var(SPRITE_ARGS) { \
    int val; \
    if ((argc != 2) && (argc != 3)) return (TCL_ERROR); \
    if (argc == 3) { \
      if (Tcl_GetInt(interp, argv[2], &val) != TCL_OK) return (TCL_ERROR); \
      sprite->var = val; \
    } \
    sprintf(interp->result, "%d", sprite->var); \
    return (TCL_OK); \
  }

#define SPRITECMD_GET_STR(var) \
  int SpriteCmd##var(SPRITE_ARGS) { \
    sprintf(interp->result, "%s", sprite->var); \
    return (TCL_OK); \
  }

int DoSpriteCmd(CLIENT_ARGS)
{
    SimSprite *sprite = (SimSprite *) clientData;
    Tcl_HashEntry *ent;
    int result = TCL_OK;
    int (*cmd) ();

    if(argc < 2)
    {
        return TCL_ERROR;
    }

    if((ent = Tcl_FindHashEntry(&SpriteCmds, argv[1])))
    {
        cmd = (int (*)()) ent->clientData;
        Tk_Preserve((ClientData) sprite);
        result = cmd(sprite, interp, argc, argv);
        Tk_Release((ClientData) sprite);
    }
    else
    {
        Tcl_AppendResult(interp, "unknown command name: \"", argv[0], " ", argv[1], "\".", (char *) NULL);
        result = TCL_ERROR;
    }
    return result;
}

int SpriteCmd(CLIENT_ARGS)
{
    SimSprite *sprite;
    int type;

    if((argc != 3) || (Tcl_GetInt(interp, argv[2], &type) != TCL_OK) || (type < 1) || (type >= OBJN))
    {
        return TCL_ERROR;
    }

    sprite = NewSprite(argv[1], type, 0, 0);
    sprite->frame = 0;

    Tcl_CreateCommand(interp, sprite->name, DoSpriteCmd, (ClientData) sprite, (void (*)()) NULL);

    interp->result = sprite->name;
    return TCL_OK;
}

SPRITECMD_GET_STR(name)
SPRITECMD_ACCESS_INT(type)
SPRITECMD_ACCESS_INT(frame)
SPRITECMD_ACCESS_INT(x)
SPRITECMD_ACCESS_INT(y)
SPRITECMD_ACCESS_INT(width)
SPRITECMD_ACCESS_INT(height)
SPRITECMD_ACCESS_INT(x_offset)
SPRITECMD_ACCESS_INT(y_offset)
SPRITECMD_ACCESS_INT(x_hot)
SPRITECMD_ACCESS_INT(y_hot)
SPRITECMD_ACCESS_INT(orig_x)
SPRITECMD_ACCESS_INT(orig_y)
SPRITECMD_ACCESS_INT(dest_x)
SPRITECMD_ACCESS_INT(dest_y)
SPRITECMD_ACCESS_INT(count)
SPRITECMD_ACCESS_INT(sound_count)
SPRITECMD_ACCESS_INT(dir)
SPRITECMD_ACCESS_INT(new_dir)
SPRITECMD_ACCESS_INT(step)
SPRITECMD_ACCESS_INT(flag)
SPRITECMD_ACCESS_INT(control) SPRITECMD_ACCESS_INT(turn) SPRITECMD_ACCESS_INT(accel) SPRITECMD_ACCESS_INT(speed)
int SpriteCmdExplode(SPRITE_ARGS)
{
    ExplodeSprite(sprite);
    return TCL_OK;
}

int SpriteCmdInit(SPRITE_ARGS)
{
    int x, y;

    if(argc != 4)
    {
        return (TCL_ERROR);
    }
    if((Tcl_GetInt(interp, argv[2], &x) != TCL_OK) ||
       (x < 0) || (x >= (WORLD_X << 4)) ||
       (Tcl_GetInt(interp, argv[3], &y) != TCL_OK) || (y < 0) || (y >= (WORLD_Y << 4)))
    {
        return (TCL_ERROR);
    }
    InitSprite(sprite, x, y);
    return TCL_OK;
}

void sprite_command_init(void)
{
    int i;

    Tcl_CreateCommand(tk_mainInterp, "sprite", SpriteCmd, (ClientData) NULL, (void (*)()) NULL);

    Tcl_InitHashTable(&SpriteCmds, TCL_STRING_KEYS);

#define SPRITE_CMD(cmd) HASHED_CMD(Sprite, cmd)

    SPRITE_CMD(name);
    SPRITE_CMD(type);
    SPRITE_CMD(frame);
    SPRITE_CMD(x);
    SPRITE_CMD(y);
    SPRITE_CMD(width);
    SPRITE_CMD(height);
    SPRITE_CMD(x_offset);
    SPRITE_CMD(y_offset);
    SPRITE_CMD(x_hot);
    SPRITE_CMD(y_hot);
    SPRITE_CMD(orig_x);
    SPRITE_CMD(orig_y);
    SPRITE_CMD(dest_x);
    SPRITE_CMD(dest_y);
    SPRITE_CMD(count);
    SPRITE_CMD(sound_count);
    SPRITE_CMD(dir);
    SPRITE_CMD(new_dir);
    SPRITE_CMD(step);
    SPRITE_CMD(flag);
    SPRITE_CMD(control);
    SPRITE_CMD(turn);
    SPRITE_CMD(accel);
    SPRITE_CMD(speed);
    SPRITE_CMD(Explode);
    SPRITE_CMD(Init);

    for(i = 0; i < OBJN; i++)
    {
        GlobalSprites[i] = NULL;
    }
}

SimSprite *FreeSprites = NULL;

SimSprite *NewSprite(char *name, int type, int x, int y)
{
    SimSprite *sprite;

    if(FreeSprites)
    {
        sprite = FreeSprites;
        FreeSprites = sprite->next;
    }
    else
    {
        sprite = (SimSprite *) ckalloc(sizeof(SimSprite));
    }

    sprite->name = (char *) ckalloc(strlen(name) + 1);
    strcpy(sprite->name, name);
    sprite->type = type;

    InitSprite(sprite, x, y);

    sim->sprites++;
    sprite->next = sim->sprite;
    sim->sprite = sprite;

    return sprite;
}

void InitSprite(SimSprite * sprite, int x, int y)
{
    sprite->x = x;
    sprite->y = y;
    sprite->frame = 0;
    sprite->orig_x = sprite->orig_y = 0;
    sprite->dest_x = sprite->dest_y = 0;
    sprite->count = sprite->sound_count = 0;
    sprite->dir = sprite->new_dir = 0;
    sprite->step = sprite->flag = 0;
    sprite->control = -1;
    sprite->turn = 0;
    sprite->accel = 0;
    sprite->speed = 100;

    if(GlobalSprites[sprite->type] == NULL)
    {
        GlobalSprites[sprite->type] = sprite;
    }

    switch (sprite->type)
    {

        case TRA:
            sprite->width = sprite->height = 32;
            sprite->x_offset = 32;
            sprite->y_offset = -16;
            sprite->x_hot = 40;
            sprite->y_hot = -8;
            sprite->frame = 1;
            sprite->dir = 4;
            break;

        case SHI:
            sprite->width = sprite->height = 48;
            sprite->x_offset = 32;
            sprite->y_offset = -16;
            sprite->x_hot = 48;
            sprite->y_hot = 0;
            if(x < (4 << 4))
                sprite->frame = 3;
            else if(x >= ((WORLD_X - 4) << 4))
                sprite->frame = 7;
            else if(y < (4 << 4))
                sprite->frame = 5;
            else if(y >= ((WORLD_Y - 4) << 4))
                sprite->frame = 1;
            else
                sprite->frame = 3;
            sprite->new_dir = sprite->frame;
            sprite->dir = 10;
            sprite->count = 1;
            break;

        case GOD:
            sprite->width = sprite->height = 48;
            sprite->x_offset = 24;
            sprite->y_offset = 0;
            sprite->x_hot = 40;
            sprite->y_hot = 16;
            if(x > ((WORLD_X << 4) / 2))
            {
                if(y > ((WORLD_Y << 4) / 2))
                    sprite->frame = 10;
                else
                    sprite->frame = 7;
            }
            else if(y > ((WORLD_Y << 4) / 2))
                sprite->frame = 1;
            else
                sprite->frame = 4;
            sprite->count = 1000;
            sprite->dest_x = PolMaxX << 4;
            sprite->dest_y = PolMaxY << 4;
            sprite->orig_x = sprite->x;
            sprite->orig_y = sprite->y;
            break;

        case COP:
            sprite->width = sprite->height = 32;
            sprite->x_offset = 32;
            sprite->y_offset = -16;
            sprite->x_hot = 40;
            sprite->y_hot = -8;
            sprite->frame = 5;
            sprite->count = 1500;
            sprite->dest_x = Rand((WORLD_X << 4) - 1);
            sprite->dest_y = Rand((WORLD_Y << 4) - 1);
            sprite->orig_x = x - 30;
            sprite->orig_y = y;
            break;

        case AIR:
            sprite->width = sprite->height = 48;
            sprite->x_offset = 24;
            sprite->y_offset = 0;
            sprite->x_hot = 48;
            sprite->y_hot = 16;
            if(x > ((WORLD_X - 20) << 4))
            {
                sprite->x -= 100 + 48;
                sprite->dest_x = sprite->x - 200;
                sprite->frame = 7;
            }
            else
            {
                sprite->dest_x = sprite->x + 200;
                sprite->frame = 11;
            }
            sprite->dest_y = sprite->y;
            break;

        case TOR:
            sprite->width = sprite->height = 48;
            sprite->x_offset = 24;
            sprite->y_offset = 0;
            sprite->x_hot = 40;
            sprite->y_hot = 36;
            sprite->frame = 1;
            sprite->count = 200;
            break;

        case EXP:
            sprite->width = sprite->height = 48;
            sprite->x_offset = 24;
            sprite->y_offset = 0;
            sprite->x_hot = 40;
            sprite->y_hot = 16;
            sprite->frame = 1;
            break;

        case BUS:
            sprite->width = sprite->height = 32;
            sprite->x_offset = 30;
            sprite->y_offset = -18;
            sprite->x_hot = 40;
            sprite->y_hot = -8;
            sprite->frame = 1;
            sprite->dir = 1;
            break;

    }
}

void DestroyAllSprites(void)
{
    SimSprite *sprite;

    for(sprite = sim->sprite; sprite != NULL; sprite = sprite->next)
    {
        sprite->frame = 0;
    }
}

void DestroySprite(SimSprite * sprite)
{
    SimView *view;
    SimSprite **sp;

    for(view = sim->editor; view != NULL; view = view->next)
        if(view->follow == sprite)
            view->follow = NULL;

    if(GlobalSprites[sprite->type] == sprite)
    {
        GlobalSprites[sprite->type] = (SimSprite *) NULL;
    }

    if(sprite->name != NULL)
    {
        ckfree(sprite->name);
        sprite->name = NULL;
    }

    for(sp = &sim->sprite; *sp != NULL; sp = &((*sp)->next))
    {
        if(sprite == (*sp))
        {
            *sp = sprite->next;
            break;
        }
    }

    sprite->next = FreeSprites;
    FreeSprites = sprite;
}

SimSprite *GetSprite(int type)
{
    SimSprite *sprite;

    if(((sprite = GlobalSprites[type]) == NULL) || (sprite->frame == 0))
        return (SimSprite *) NULL;
    else
        return sprite;
}

SimSprite *MakeSprite(int type, int x, int y)
{
    SimSprite *sprite;

    if((sprite = GlobalSprites[type]) == NULL)
    {
        sprite = NewSprite("", type, x, y);
    }
    else
    {
        InitSprite(sprite, x, y);
    }
    return sprite;
}

SimSprite *MakeNewSprite(int type, int x, int y)
{
    SimSprite *sprite;

    sprite = NewSprite("", type, x, y);
    return sprite;
}

void DrawObjects(SimView * view)
{
    SimSprite *sprite;

    /*
     * XXX: sort these by layer 
     */
/*
  if (z = Oframe[TRA]) DrawTrain(view, z);
  if (z = Oframe[SHI]) DrawBoat(view, z);
  if (z = Oframe[GOD]) DrawMonster(view, z);
  if (z = Oframe[COP]) DrawCopter(view, z);
  if (z = Oframe[AIR]) DrawPlane(view, z);
  if (z = Oframe[TOR]) DrawTor(view, z);
  if (z = Oframe[EXP]) DrawExp(view, z);
*/

    for(sprite = sim->sprite; sprite != NULL; sprite = sprite->next)
    {
        DrawSprite(view, sprite);
    }
}

void DrawSprite(SimView * view, SimSprite * sprite)
{
    Pixmap pict, mask;
    int x, y, i;

    if(sprite->frame == 0)
        return;

    i = (sprite->frame - 1) * 2;
    pict = view->x->objects[sprite->type][i];
    mask = view->x->objects[sprite->type][i + 1];

    x = sprite->x - ((view->tile_x << 4) - view->screen_x) + sprite->x_offset;
    y = sprite->y - ((view->tile_y << 4) - view->screen_y) + sprite->y_offset;

    XSetClipMask(view->x->dpy, view->x->gc, mask);
    XSetClipOrigin(view->x->dpy, view->x->gc, x, y);
    XCopyArea(view->x->dpy, pict, view->pixmap2, view->x->gc, 0, 0, sprite->width, sprite->height, x, y);
    XSetClipMask(view->x->dpy, view->x->gc, None);
    XSetClipOrigin(view->x->dpy, view->x->gc, 0, 0);
}

short GetChar(int x, int y)
{
    x >>= 4;
    y >>= 4;
    if(!TestBounds(x, y))
        return (-1);
    else
        return (Map[x][y] & LOMASK);
}

short TurnTo(int p, int d)
{
    if(p == d)
        return (p);
    if(p < d)
        if((d - p) < 4)
            p++;
        else
            p--;
    else if((p - d) < 4)
        p--;
    else
        p++;
    if(p > 8)
        p = 1;
    if(p < 1)
        p = 8;
    return (p);
}

int TryOther(int Tpoo, int Told, int Tnew)
{
    register short z;

    z = Told + 4;
    if(z > 8)
        z -= 8;
    if(Tnew != z)
        return (0);
    if((Tpoo == POWERBASE) || (Tpoo == POWERBASE + 1) || (Tpoo == RAILBASE) || (Tpoo == RAILBASE + 1))
        return (1);
    return (0);
}

short SpriteNotInBounds(SimSprite * sprite)
{
    int x = sprite->x + sprite->x_hot;
    int y = sprite->y + sprite->y_hot;

    if((x < 0) || (y < 0) || (x >= (WORLD_X << 4)) || (y >= (WORLD_Y << 4)))
    {
        return (1);
    }
    return (0);
}

short GetDir(int orgX, int orgY, int desX, int desY)
{
    static short Gdtab[13] = { 0, 3, 2, 1, 3, 4, 5, 7, 6, 5, 7, 8, 1 };
    int dispX, dispY, z;

    dispX = desX - orgX;
    dispY = desY - orgY;
    if(dispX < 0)
        if(dispY < 0)
            z = 11;
        else
            z = 8;
    else if(dispY < 0)
        z = 2;
    else
        z = 5;
    if(dispX < 0)
        dispX = -dispX;
    if(dispY < 0)
        dispY = -dispY;

    absDist = dispX + dispY;

    if((dispX << 1) < dispY)
        z++;
    else if((dispY << 1) < dispY)
        z--;

    if((z < 0) || (z > 12))
        z = 0;

    return (Gdtab[z]);
}

int GetDis(int x1, int y1, int x2, int y2)
{
    register short dispX, dispY;

    if(x1 > x2)
        dispX = x1 - x2;
    else
        dispX = x2 - x1;
    if(y1 > y2)
        dispY = y1 - y2;
    else
        dispY = y2 - y1;

    return (dispX + dispY);
}

int CheckSpriteCollision(SimSprite * s1, SimSprite * s2)
{
    if((s1->frame != 0) && (s2->frame != 0) &&
       GetDis(s1->x + s1->x_hot, s1->y + s1->y_hot, s2->x + s2->x_hot, s2->y + s2->y_hot) < 30)
        return (1);
    return (0);
}

void MoveObjects(void)
{
    SimSprite *sprite;

    if(!SimSpeed)
        return;
    Cycle++;

    for(sprite = sim->sprite; sprite != NULL;)
    {
        if(sprite->frame)
        {
            switch (sprite->type)
            {
                case TRA:
                    DoTrainSprite(sprite);
                    break;
                case COP:
                    DoCopterSprite(sprite);
                    break;
                case AIR:
                    DoAirplaneSprite(sprite);
                    break;
                case SHI:
                    DoShipSprite(sprite);
                    break;
                case GOD:
                    DoMonsterSprite(sprite);
                    break;
                case TOR:
                    DoTornadoSprite(sprite);
                    break;
                case EXP:
                    DoExplosionSprite(sprite);
                    break;
                case BUS:
                    DoBusSprite(sprite);
                    break;
            }
            sprite = sprite->next;
        }
        else
        {
            if(sprite->name[0] == '\0')
            {
                SimSprite *s = sprite;

                sprite = sprite->next;
                DestroySprite(s);
            }
            else
            {
                sprite = sprite->next;
            }
        }
    }
}

void DoTrainSprite(SimSprite * sprite)
{
    static short Cx[4] = { 0, 16, 0, -16 };
    static short Cy[4] = { -16, 0, 16, 0 };
    static short Dx[5] = { 0, 4, 0, -4, 0 };
    static short Dy[5] = { -4, 0, 4, 0, 0 };
    static short TrainPic2[5] = { 1, 2, 1, 2, 5 };
    register short z, dir, dir2;
    short c;

    if((sprite->frame == 3) || (sprite->frame == 4))
        sprite->frame = TrainPic2[sprite->dir];
    sprite->x += Dx[sprite->dir];
    sprite->y += Dy[sprite->dir];
    if(!(Cycle & 3))
    {
        dir = Rand16() & 3;
        for(z = dir; z < (dir + 4); z++)
        {
            dir2 = z & 3;
            if(sprite->dir != 4)
            {
                if(dir2 == ((sprite->dir + 2) & 3))
                    continue;
            }
            c = GetChar(sprite->x + Cx[dir2] + 48, sprite->y + Cy[dir2]);
            if(((c >= RAILBASE) && (c <= LASTRAIL)) ||  /* track? */
               (c == RAILVPOWERH) || (c == RAILHPOWERV))
            {
                if((sprite->dir != dir2) && (sprite->dir != 4))
                {
                    if((sprite->dir + dir2) == 3)
                        sprite->frame = 3;
                    else
                        sprite->frame = 4;
                }
                else
                    sprite->frame = TrainPic2[dir2];

                if((c == RAILBASE) || (c == (RAILBASE + 1)))
                    sprite->frame = 5;
                sprite->dir = dir2;
                return;
            }
        }
        if(sprite->dir == 4)
        {
            sprite->frame = 0;
            return;
        }
        sprite->dir = 4;
    }
}

void DoCopterSprite(SimSprite * sprite)
{
    static short CDx[9] = { 0, 0, 3, 5, 3, 0, -3, -5, -3 };
    static short CDy[9] = { 0, -5, -3, 0, 3, 5, 3, 0, -3 };
    register short z, d, x, y;

    if(sprite->sound_count > 0)
        sprite->sound_count--;

    if(sprite->control < 0)
    {

        if(sprite->count > 0)
            sprite->count--;

        if(!sprite->count)
        {
            /*
             * Attract copter to monster and tornado so it blows up more often 
             */
            SimSprite *s = GetSprite(GOD);

            if(s != NULL)
            {
                sprite->dest_x = s->x;
                sprite->dest_y = s->y;
            }
            else
            {
                s = GetSprite(TOR);
                if(s != NULL)
                {
                    sprite->dest_x = s->x;
                    sprite->dest_y = s->y;
                }
                else
                {
                    sprite->dest_x = sprite->orig_x;
                    sprite->dest_y = sprite->orig_y;
                }
            }
        }
        if(!sprite->count)
        {                       /* land */
            GetDir(sprite->x, sprite->y, sprite->orig_x, sprite->orig_y);
            if(absDist < 30)
            {
                sprite->frame = 0;
                return;
            }
        }
    }
    else
    {
        GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y);
        if(absDist < 16)
        {
            sprite->dest_x = sprite->orig_x;
            sprite->dest_y = sprite->orig_y;
            sprite->control = -1;
        }
    }

    if(!sprite->sound_count)
    {                           /* send report  */
        x = (sprite->x + 48) >> 5;
        y = sprite->y >> 5;
        if((x >= 0) && (x < (WORLD_X >> 1)) && (y >= 0) && (y < (WORLD_Y >> 1)))
        {
            /*
             * Don changed from 160 to 170 to shut the #$%#$% thing up! 
             */
            if((TrfDensity[x][y] > 200) && ((Rand16() & 7) == 0))
            {
                SendMesAt(-41, (x << 1) + 1, (y << 1) + 1);
                MakeSound("city", "HeavyTraffic");  /* chopper */
                sprite->sound_count = 200;
            }
        }
    }
    z = sprite->frame;
    if(!(Cycle & 3))
    {
        d = GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y);
        z = TurnTo(z, d);
        sprite->frame = z;
    }

    sprite->x += CDx[z];
    sprite->y += CDy[z];
}

void DoAirplaneSprite(SimSprite * sprite)
{
    static short CDx[12] = { 0, 0, 6, 8, 6, 0, -6, -8, -6, 8, 8, 8 };
    static short CDy[12] = { 0, -8, -6, 0, 6, 8, 6, 0, -6, 0, 0, 0 };

    register short z, d;
    SimSprite *s;
    int explode = 0;

    z = sprite->frame;

    if(!(Cycle % 5))
    {
        if(z > 8)
        {                       /* TakeOff  */
            z--;
            if(z < 9)
                z = 3;
            sprite->frame = z;
        }
        else
        {                       /* goto destination */
            d = GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y);
            z = TurnTo(z, d);
            sprite->frame = z;
        }
    }

    if(absDist < 50)
    {                           /* at destination  */
        sprite->dest_x = Rand((WORLD_X * 16) + 100) - 50;
        sprite->dest_y = Rand((WORLD_Y * 16) + 100) - 50;
    }

    for(s = sim->sprite; s != NULL; s = s->next)
    {
        if((s->frame != 0) &&
           ((s->type == COP) || ((sprite != s) && (s->type == AIR))) && CheckSpriteCollision(sprite, s))
        {
            ExplodeSprite(s);
            explode = 1;
        }
    }

    if(explode)
        ExplodeSprite(sprite);

    sprite->x += CDx[z];
    sprite->y += CDy[z];
    if(SpriteNotInBounds(sprite))
        sprite->frame = 0;
}

void DoShipSprite(SimSprite * sprite)
{
    static short BDx[9] = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
    static short BDy[9] = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
    static short BPx[9] = { 0, 0, 2, 2, 2, 0, -2, -2, -2 };
    static short BPy[9] = { 0, -2, -2, 0, 2, 2, 2, 0, -2 };
    static short BtClrTab[8] = { RIVER, CHANNEL, POWERBASE, POWERBASE + 1,
        RAILBASE, RAILBASE + 1, BRWH, BRWV
    };
    register short x, y, z, t = RIVER;
    short tem, pem;

    if(sprite->sound_count > 0)
        sprite->sound_count--;
    if(!sprite->sound_count)
    {
        if((Rand16() & 3) == 1)
        {
            if((ScenarioID == 2) && /* San Francisco */
               (Rand(10) < 5))
            {
                MakeSound("city", "HonkHonk-Low");
            }
            else
            {
                MakeSound("city", "HonkHonk-Low");
            }
        }
        sprite->sound_count = 200;
    }

    if(sprite->count > 0)
        sprite->count--;
    if(!sprite->count)
    {
        sprite->count = 9;
        if(sprite->frame != sprite->new_dir)
        {
            sprite->frame = TurnTo(sprite->frame, sprite->new_dir);
            return;
        }
        tem = Rand16() & 7;
        for(pem = tem; pem < (tem + 8); pem++)
        {
            z = (pem & 7) + 1;

            if(z == sprite->dir)
                continue;
            x = ((sprite->x + (48 - 1)) >> 4) + BDx[z];
            y = (sprite->y >> 4) + BDy[z];
            if(TestBounds(x, y))
            {
                t = Map[x][y] & LOMASK;
                if((t == CHANNEL) || (t == BRWH) || (t == BRWV) || TryOther(t, sprite->dir, z))
                {
                    sprite->new_dir = z;
                    sprite->frame = TurnTo(sprite->frame, sprite->new_dir);
                    sprite->dir = z + 4;
                    if(sprite->dir > 8)
                        sprite->dir -= 8;
                    break;
                }
            }
        }
        if(pem == (tem + 8))
        {
            sprite->dir = 10;
            sprite->new_dir = (Rand16() & 7) + 1;
        }
    }
    else
    {
        z = sprite->frame;
        if(z == sprite->new_dir)
        {
            sprite->x += BPx[z];
            sprite->y += BPy[z];
        }
    }
    if(SpriteNotInBounds(sprite))
    {
        sprite->frame = 0;
        return;
    }

    for(z = 0; z < 8; z++)
    {
        if(t == BtClrTab[z])
            break;
        if(z == 7)
        {
            ExplodeSprite(sprite);
            Destroy(sprite->x + 48, sprite->y);
        }
    }
}

void DoMonsterSprite(SimSprite * sprite)
{
    static short Gx[5] = { 2, 2, -2, -2, 0 };
    static short Gy[5] = { -2, 2, 2, -2, 0 };
    static short ND1[4] = { 0, 1, 2, 3 };
    static short ND2[4] = { 1, 2, 3, 0 };
    static short nn1[4] = { 2, 5, 8, 11 };
    static short nn2[4] = { 11, 2, 5, 8 };
    register short d, z, c;

    if(sprite->sound_count > 0)
        sprite->sound_count--;

    if(sprite->control < 0)
    {
        /*
         * business as usual 
         */

        if(sprite->control == -2)
        {
            d = (sprite->frame - 1) / 3;
            z = (sprite->frame - 1) % 3;
            if(z == 2)
                sprite->step = 0;
            if(z == 0)
                sprite->step = 1;
            if(sprite->step)
                z++;
            else
                z--;
            c = GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y);
            if(absDist < 18)
            {
                sprite->control = -1;
                sprite->count = 1000;
                sprite->flag = 1;
                sprite->dest_x = sprite->orig_x;
                sprite->dest_y = sprite->orig_y;
            }
            else
            {
                c = (c - 1) / 2;
                if(((c != d) && (!Rand(5))) || (!Rand(20)))
                {
                    int diff = (c - d) & 3;

                    if((diff == 1) || (diff == 3))
                    {
                        d = c;
                    }
                    else
                    {
                        if(Rand16() & 1)
                            d++;
                        else
                            d--;
                        d &= 3;
                    }
                }
                else
                {
                    if(!Rand(20))
                    {
                        if(Rand16() & 1)
                            d++;
                        else
                            d--;
                        d &= 3;
                    }
                }
            }
        }
        else
        {

            d = (sprite->frame - 1) / 3;

            if(d < 4)
            {                   /* turn n s e w */
                z = (sprite->frame - 1) % 3;
                if(z == 2)
                    sprite->step = 0;
                if(z == 0)
                    sprite->step = 1;
                if(sprite->step)
                    z++;
                else
                    z--;
                GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y);
                if(absDist < 60)
                {
                    if(sprite->flag == 0)
                    {
                        sprite->flag = 1;
                        sprite->dest_x = sprite->orig_x;
                        sprite->dest_y = sprite->orig_y;
                    }
                    else
                    {
                        sprite->frame = 0;
                        return;
                    }
                }
                c = GetDir(sprite->x, sprite->y, sprite->dest_x, sprite->dest_y);
                c = (c - 1) / 2;
                if((c != d) && (!Rand(10)))
                {
                    if(Rand16() & 1)
                        z = ND1[d];
                    else
                        z = ND2[d];
                    d = 4;
                    if(!sprite->sound_count)
                    {
                        MakeSound("city", "Monster");   /* monster */
                        sprite->sound_count = 50 + Rand(100);
                    }
                }
            }
            else
            {
                d = 4;
                c = sprite->frame;
                z = (c - 13) & 3;
                if(!(Rand16() & 3))
                {
                    if(Rand16() & 1)
                        z = nn1[z];
                    else
                        z = nn2[z];
                    d = (z - 1) / 3;
                    z = (z - 1) % 3;
                }
            }
        }
    }
    else
    {
        /*
         * somebody's taken control of the monster 
         */

        d = sprite->control;
        z = (sprite->frame - 1) % 3;

        if(z == 2)
            sprite->step = 0;
        if(z == 0)
            sprite->step = 1;
        if(sprite->step)
            z++;
        else
            z--;
    }

    z = (((d * 3) + z) + 1);
    if(z > 16)
        z = 16;
    sprite->frame = z;

    sprite->x += Gx[d];
    sprite->y += Gy[d];

    if(sprite->count > 0)
        sprite->count--;
    c = GetChar(sprite->x + sprite->x_hot, sprite->y + sprite->y_hot);
    if((c == -1)
#ifndef ORIGINAL_MONSTER_BEHAVIOUR
       || ((c == RIVER) && (sprite->count != 0) && (sprite->count < 900) && (sprite->control == -1))
#endif
        )
    {
        sprite->frame = 0;      /* kill zilla */
    }

    {
        SimSprite *s;

        for(s = sim->sprite; s != NULL; s = s->next)
        {
            if((s->frame != 0) &&
               ((s->type == AIR) ||
                (s->type == COP) || (s->type == SHI) || (s->type == TRA)) && CheckSpriteCollision(sprite, s))
            {
                ExplodeSprite(s);
            }
        }
    }

    Destroy(sprite->x + 48, sprite->y + 16);
}

void DoTornadoSprite(SimSprite * sprite)
{
    static short CDx[9] = { 2, 3, 2, 0, -2, -3 };
    static short CDy[9] = { -2, 0, 2, 3, 2, 0 };
    register short z;

    z = sprite->frame;

    if(z == 2)                  /* cycle animation... post Rel */
        if(sprite->flag)
            z = 3;
        else
            z = 1;
    else
    {
        if(z == 1)
            sprite->flag = 1;
        else
            sprite->flag = 0;
        z = 2;
    }

    if(sprite->count > 0)
        sprite->count--;

    sprite->frame = z;

    {
        SimSprite *s;

        for(s = sim->sprite; s != NULL; s = s->next)
        {
            if((s->frame != 0) &&
               ((s->type == AIR) ||
                (s->type == COP) || (s->type == SHI) || (s->type == TRA)) && CheckSpriteCollision(sprite, s))
            {
                ExplodeSprite(s);
            }
        }
    }

    z = Rand(5);
    sprite->x += CDx[z];
    sprite->y += CDy[z];
    if(SpriteNotInBounds(sprite))
        sprite->frame = 0;

    if((sprite->count != 0) && (!Rand(500)))
        sprite->frame = 0;

    Destroy(sprite->x + 48, sprite->y + 40);
}

void DoExplosionSprite(SimSprite * sprite)
{
    short x, y;

    if(!(Cycle & 1))
    {
        if(sprite->frame == 1)
        {
            MakeSound("city", "Explosion-High");    /* explosion */
            x = (sprite->x >> 4) + 3;
            y = (sprite->y >> 4);
            SendMesAt(32, x, y);
        }
        sprite->frame++;
    }

    if(sprite->frame > 6)
    {
        sprite->frame = 0;

        StartFire(sprite->x + 48 - 8, sprite->y + 16);
        StartFire(sprite->x + 48 - 24, sprite->y);
        StartFire(sprite->x + 48 + 8, sprite->y);
        StartFire(sprite->x + 48 - 24, sprite->y + 32);
        StartFire(sprite->x + 48 + 8, sprite->y + 32);
        return;
    }
}

void DoBusSprite(SimSprite * sprite)
{
    static short Dx[5] = { 0, 1, 0, -1, 0 };
    static short Dy[5] = { -1, 0, 1, 0, 0 };
    static short Dir2Frame[4] = { 1, 2, 1, 2 };
    int dx, dy, tx, ty, otx, oty;
    int turned = 0;
    int speed = 0, z;

    SimSprite *s;
    int explode = 0;

#ifdef DEBUGBUS
    printf("Bus dir %d turn %d frame %d\n", sprite->dir, sprite->turn, sprite->frame);
#endif

    if(sprite->turn)
    {
        if(sprite->turn < 0)
        {                       /* ccw */
            if(sprite->dir & 1)
            {                   /* up or down */
                sprite->frame = 4;
            }
            else
            {                   /* left or right */
                sprite->frame = 3;
            }
            sprite->turn++;
            sprite->dir = (sprite->dir - 1) & 3;
        }
        else
        {                       /* cw */
            if(sprite->dir & 1)
            {                   /* up or down */
                sprite->frame = 3;
            }
            else
            {                   /* left or right */
                sprite->frame = 4;
            }
            sprite->turn--;
            sprite->dir = (sprite->dir + 1) & 3;
        }
        turned = 1;
    }
    else
    {
        /*
         * finish turn 
         */
        if((sprite->frame == 3) || (sprite->frame == 4))
        {
            turned = 1;
            sprite->frame = Dir2Frame[sprite->dir];
        }
    }

    if(sprite->speed == 0)
    {
        /*
         * brake 
         */
        dx = 0;
        dy = 0;
    }
    else
    {                           /* cruise at traffic speed */

        tx = (sprite->x + sprite->x_hot) >> 5;
        ty = (sprite->y + sprite->y_hot) >> 5;
        if((tx >= 0) && (tx < (WORLD_X >> 1)) && (ty >= 0) && (ty < (WORLD_Y >> 1)))
        {
            z = TrfDensity[tx][ty] >> 6;
            if(z > 1)
                z--;
        }
        else
            z = 0;

        switch (z)
        {
            case 0:
                speed = 8;
                break;
            case 1:
                speed = 4;
                break;
            case 2:
                speed = 1;
                break;
        }

        /*
         * govern speed 
         */
        if(speed > sprite->speed)
            speed = sprite->speed;

        if(turned)
        {
#ifdef DEBUGBUS
            printf("turned\n");
#endif
            if(speed > 1)
                speed = 1;
            dx = Dx[sprite->dir] * speed;
            dy = Dy[sprite->dir] * speed;
        }
        else
        {
            dx = Dx[sprite->dir] * speed;
            dy = Dy[sprite->dir] * speed;

            tx = (sprite->x + sprite->x_hot) >> 4;
            ty = (sprite->y + sprite->y_hot) >> 4;

            /*
             * drift into the right lane 
             */
            switch (sprite->dir)
            {
                case 0:        /* up */
                    z = ((tx << 4) + 4) - (sprite->x + sprite->x_hot);
                    if(z < 0)
                        dx = -1;
                    else if(z > 0)
                        dx = 1;
#ifdef DEBUGBUS
                    printf("moving up x %x z %d dx %d\n", sprite->x + sprite->x_hot, z, dx);
#endif
                    break;
                case 1:        /* right */
                    z = ((ty << 4) + 4) - (sprite->y + sprite->y_hot);
                    if(z < 0)
                        dy = -1;
                    else if(z > 0)
                        dy = 1;
#ifdef DEBUGBUS
                    printf("moving right y %x z %d dy %d\n", sprite->y + sprite->y_hot, z, dy);
#endif
                    break;
                case 2:        /* down */
                    z = ((tx << 4)) - (sprite->x + sprite->x_hot);
                    if(z < 0)
                        dx = -1;
                    else if(z > 0)
                        dx = 1;
#ifdef DEBUGBUS
                    printf("moving down x %x z %d dx %d\n", sprite->x + sprite->x_hot, z, dx);
#endif
                    break;
                case 3:        /* left */
                    z = ((ty << 4)) - (sprite->y + sprite->y_hot);
                    if(z < 0)
                        dy = -1;
                    else if(z > 0)
                        dy = 1;
#ifdef DEBUGBUS
                    printf("moving left y %x z %d dy %d\n", sprite->y + sprite->y_hot, z, dy);
#endif
                    break;
            }
        }
    }
#ifdef DEBUGBUS
    printf("speed dx %d dy %d\n", dx, dy);
#endif

#define AHEAD 8

    otx = (sprite->x + sprite->x_hot + (Dx[sprite->dir] * AHEAD)) >> 4;
    oty = (sprite->y + sprite->y_hot + (Dy[sprite->dir] * AHEAD)) >> 4;
    if(otx < 0)
        otx = 0;
    else if(otx >= WORLD_X)
        otx = WORLD_X - 1;
    if(oty < 0)
        oty = 0;
    else if(oty >= WORLD_Y)
        oty = WORLD_Y - 1;

    tx = (sprite->x + sprite->x_hot + dx + (Dx[sprite->dir] * AHEAD)) >> 4;
    ty = (sprite->y + sprite->y_hot + dy + (Dy[sprite->dir] * AHEAD)) >> 4;
    if(tx < 0)
        tx = 0;
    else if(tx >= WORLD_X)
        tx = WORLD_X - 1;
    if(ty < 0)
        ty = 0;
    else if(ty >= WORLD_Y)
        ty = WORLD_Y - 1;

    if((tx != otx) || (ty != oty))
    {
#ifdef DEBUGBUS
        printf("drive from tile %d %d to %d %d\n", otx, oty, tx, ty);
#endif
        z = CanDriveOn(tx, ty);
        if(z == 0)
        {
            /*
             * can't drive forward into a new tile 
             */
            if(speed == 8)
            {
                bulldozer_tool(NULL, tx, ty);
            }
            else
            {
            }
        }
        else
        {
            /*
             * drive forward into a new tile 
             */
            if(z > 0)
            {
                /*
                 * smooth 
                 */
            }
            else
            {
                /*
                 * bumpy 
                 */
                dx /= 2;
                dy /= 2;
            }
        }
    }

    tx = (sprite->x + sprite->x_hot + dx) >> 4;
    ty = (sprite->y + sprite->y_hot + dy) >> 4;
    z = CanDriveOn(tx, ty);
    if(z > 0)
    {
        /*
         * cool, cruise along 
         */
    }
    else
    {
        if(z < 0)
        {
            /*
             * bumpy 
             */
        }
        else
        {
            /*
             * something in the way 
             */
        }
    }

    sprite->x += dx;
    sprite->y += dy;

    for(s = sim->sprite; s != NULL; s = s->next)
    {
        if((sprite != s) &&
           (s->frame != 0) &&
           ((s->type == BUS) || ((s->type == TRA) && (s->frame != 5))) && CheckSpriteCollision(sprite, s))
        {
            ExplodeSprite(s);
            explode = 1;
        }
    }
    if(explode)
        ExplodeSprite(sprite);
}

int CanDriveOn(int x, int y)
{
    int tile;

    if(!TestBounds(x, y))
        return 0;

    tile = Map[x][y] & LOMASK;

    if(((tile >= ROADBASE) &&
        (tile <= LASTROAD) && (tile != BRWH) && (tile != BRWV)) || (tile == HRAILROAD) || (tile == VRAILROAD))
        return 1;

    if((tile == DIRT) || tally(tile))
        return -1;

    return 0;
}

void ExplodeSprite(SimSprite * sprite)
{
    int x, y;

    sprite->frame = 0;

    x = sprite->x + sprite->x_hot;
    y = sprite->y + sprite->y_hot;
    MakeExplosionAt(x, y);

    x = (x >> 4);
    y = (y >> 4);

    switch (sprite->type)
    {
        case AIR:
            CrashX = x;
            CrashY = y;
            SendMesAt(-24, x, y);
            break;
        case SHI:
            CrashX = x;
            CrashY = y;
            SendMesAt(-25, x, y);
            break;
        case TRA:
            CrashX = x;
            CrashY = y;
            SendMesAt(-26, x, y);
            break;
        case COP:
            CrashX = x;
            CrashY = y;
            SendMesAt(-27, x, y);
            break;
        case BUS:
            CrashX = x;
            CrashY = y;
            SendMesAt(-26, x, y);   /* XXX for now */
            break;
    }
    MakeSound("city", "Explosion-High");    /* explosion */
    return;
}

int checkWet(int x)
{
    if((x == POWERBASE) || (x == POWERBASE + 1) || (x == RAILBASE) || (x == RAILBASE + 1) || (x == BRWH) || (x == BRWV))
        return (1);
    else
        return (0);
}

void Destroy(int ox, int oy)
{
    short t, z, x, y;

    x = ox >> 4;
    y = oy >> 4;
    if(!TestBounds(x, y))
        return;
    z = Map[x][y];
    t = z & LOMASK;
    if(t >= TREEBASE)
    {
        /*
         * TILE_IS_BRIDGE(t) 
         */
        if(!(z & BURNBIT))
        {
            if((t >= ROADBASE) && (t <= LASTROAD))
                Map[x][y] = RIVER;
            return;
        }
        if(z & ZONEBIT)
        {
            OFireZone(x, y, z);
            if(t > RZB)
            {
                MakeExplosionAt(ox, oy);
            }
        }
        if(checkWet(t))
            Map[x][y] = RIVER;
        else
            Map[x][y] = (DoAnimation ? TINYEXP : (LASTTINYEXP - 3)) | BULLBIT | ANIMBIT;
    }
}

void OFireZone(int Xloc, int Yloc, int ch)
{
    register short Xtem, Ytem;
    short x, y, XYmax;

    RateOGMem[Xloc >> 3][Yloc >> 3] -= 20;

    ch &= LOMASK;
    if(ch < PORTBASE)
        XYmax = 2;
    else if(ch == AIRPORT)
        XYmax = 5;
    else
        XYmax = 4;

    for(x = -1; x < XYmax; x++)
        for(y = -1; y < XYmax; y++)
        {
            Xtem = Xloc + x;
            Ytem = Yloc + y;
            if((Map[Xtem][Ytem] & LOMASK) >= ROADBASE)
                Map[Xtem][Ytem] |= BULLBIT;
        }
}

void StartFire(int x, int y)
{
    register int t, z;

    x >>= 4;
    y >>= 4;
    if((x >= WORLD_X) || (y >= WORLD_Y) || (x < 0) || (y < 0))
        return;
    z = Map[x][y];
    t = z & LOMASK;
    if((!(z & BURNBIT)) && (t != 0))
        return;
    if(z & ZONEBIT)
        return;
    Map[x][y] = FIRE + (Rand16() & 3) + ANIMBIT;
}

void GenerateTrain(int x, int y)
{
    if((TotalPop > 20) && (GetSprite(TRA) == NULL) && (!Rand(25)))
    {
        MakeSprite(TRA, (x << 4) + TRA_GROOVE_X, (y << 4) + TRA_GROOVE_Y);
    }
}

void GenerateBus(int x, int y)
{
    if((GetSprite(BUS) == NULL) && (!Rand(25)))
    {
        MakeSprite(BUS, (x << 4) + BUS_GROOVE_X, (y << 4) + BUS_GROOVE_Y);
    }
}

void GenerateShip(void)
{
    register short x, y;

    if(!(Rand16() & 3))
        for(x = 4; x < WORLD_X - 2; x++)
            if(Map[x][0] == CHANNEL)
            {
                MakeShipHere(x, 0);
                return;
            }
    if(!(Rand16() & 3))
        for(y = 1; y < WORLD_Y - 2; y++)
            if(Map[0][y] == CHANNEL)
            {
                MakeShipHere(0, y);
                return;
            }
    if(!(Rand16() & 3))
        for(x = 4; x < WORLD_X - 2; x++)
            if(Map[x][WORLD_Y - 1] == CHANNEL)
            {
                MakeShipHere(x, WORLD_Y - 1);
                return;
            }
    if(!(Rand16() & 3))
        for(y = 1; y < WORLD_Y - 2; y++)
            if(Map[WORLD_X - 1][y] == CHANNEL)
            {
                MakeShipHere(WORLD_X - 1, y);
                return;
            }
}

void MakeShipHere(int x, int y)
{
    MakeSprite(SHI, (x << 4) - (48 - 1), (y << 4));
}

void MakeMonster(void)
{
    register int x, y, z, done = 0;
    SimSprite *sprite;

    if((sprite = GetSprite(GOD)) != NULL)
    {
        sprite->sound_count = 1;
        sprite->count = 1000;
        sprite->dest_x = PolMaxX << 4;
        sprite->dest_y = PolMaxY << 4;
        return;
    }

    for(z = 0; z < 300; z++)
    {
        x = Rand(WORLD_X - 20) + 10;
        y = Rand(WORLD_Y - 10) + 5;
        if((Map[x][y] == RIVER) || (Map[x][y] == RIVER + BULLBIT))
        {
            MonsterHere(x, y);
            done = 1;
            break;
        }
    }
    if(!done == 0)
        MonsterHere(60, 50);
}

void MonsterHere(int x, int y)
{
    MakeSprite(GOD, (x << 4) + 48, (y << 4));
    ClearMes();
    SendMesAt(-21, x + 5, y);
}

void GenerateCopter(int x, int y)
{
    if(GetSprite(COP) != NULL)
        return;

    MakeSprite(COP, (x << 4), (y << 4) + 30);
}

void GeneratePlane(int x, int y)
{
    if(GetSprite(AIR) != NULL)
        return;

    MakeSprite(AIR, (x << 4) + 48, (y << 4) + 12);
}

void MakeAirCrash(void)
{
#ifndef NO_AIRCRASH
    if(GetSprite(AIR) == NULL)
    {
        short x, y;

        x = Rand(WORLD_X - 20) + 10;
        y = Rand(WORLD_Y - 10) + 5;

        GeneratePlane(x, y);
    }

    ExplodeSprite(GetSprite(AIR));
#endif
}

void MakeTornado(void)
{
    short x, y;
    SimSprite *sprite;

    if((sprite = GetSprite(TOR)) != NULL)
    {
        sprite->count = 200;
        return;
    }

    x = Rand((WORLD_X << 4) - 800) + 400;
    y = Rand((WORLD_Y << 4) - 200) + 100;
    MakeSprite(TOR, x, y);
    ClearMes();
    SendMesAt(-22, (x >> 4) + 3, (y >> 4) + 2);
}

void MakeExplosion(int x, int y)
{
    if((x >= 0) && (x < WORLD_X) && (y >= 0) && (y < WORLD_Y))
    {
        MakeExplosionAt((x << 4) + 8, (y << 4) + 8);
    }
}

void MakeExplosionAt(int x, int y)
{
    MakeNewSprite(EXP, x - 40, y - 16);
}
